########################################################################
## MQTT业务
## 2025-08-15 harrydeep@wardyan.top
## 目前, 所有MQTT业务都必须手动在最后面注册
########################################################################

import os
import shutil
import urllib.request
import hashlib
from top.wardyan.projects.checktool.checktool import checktool

# 卡住的查询
MQTTBUSINESS_AUTOKILL_SLOWQUERY = 11011

# 被KILL掉的查询
MQTTBUSINESS_AUTOKILL_TOKILLQUERY = 11012

# 系统信息(磁盘空间,CPU,磁盘活跃时间等)
MQTTBUSINESS_AUTOKILL_SYSTEMINFO = 11013

# 业务处理函数字典
BUSINESSES = {}

def mqttbusiness(id) :
    def decorator(func):
        BUSINESSES[id] = func
        return func
    return decorator


@mqttbusiness(10106)
def handle_10106(msg : dict) :
    checktool.handle_10106(msg)






@mqttbusiness(10101)
def handle_10101(msg : dict) :
    ## 处理心跳结果
    ## msg的格式是:
    ## {'business': 10101, 'custcode': 'JTLD', 'data': {'checks': [{'sID': 21000008, 'xDataType': 1}, {'sID': 21000009, 'xDataType': 1}, {'sID': 21000010, 'xDataType': 1}, {'sID': 21000011, 'xDataType': 1}, {'sID': 21000012, 'xDataType': 1}, {'sID': 21000013, 'xDataType': 1}, {'sID': 21000014, 'xDataType': 1}, {'sID': 21000015, 'xDataType': 1}, {'sID': 21000024, 'xDataType': 1}, {'sID': 21000025, 'xDataType': 1}, {'sID': 21000027, 'xDataType': 1}, {'sID': 21000028, 'xDataType': 1}, {'sID': 21000029, 'xDataType': 1}, {'sID': 21000030, 'xDataType': 1}, {'sID': 21000031, 'xDataType': 1}, {'sID': 21000032, 'xDataType': 1}, {'sID': 21000033, 'xDataType': 1}, {'sID': 21000034, 'xDataType': 1}, {'sID': 21000035, 'xDataType': 1}, {'sID': 21000036, 'xDataType': 1}, {'sID': 21000037, 'xDataType': 1}, {'sID': 21000038, 'xDataType': 1}, {'sID': 21000039, 'xDataType': 1}, {'sID': 21000040, 'xDataType': 1}, {'sID': 21000041, 'xDataType': 1}, {'sID': 21000042, 'xDataType': 1}, {'sID': 21000043, 'xDataType': 1}, {'sID': 21000044, 'xDataType': 1}, {'sID': 21000045, 'xDataType': 1}, {'sID': 21000046, 'xDataType': 1}, {'sID': 21000047, 'xDataType': 1}, {'sID': 21000048, 'xDataType': 1}, {'sID': 21000050, 'xDataType': 1}, {'sID': 21000051, 'xDataType': 1}, {'sID': 21000052, 'xDataType': 1}, {'sID': 21000053, 'xDataType': 1}, {'sID': 21000054, 'xDataType': 1}, {'sID': 21000055, 'xDataType': 1}, {'sID': 21000056, 'xDataType': 1}, {'sID': 21000057, 'xDataType': 1}, {'sID': 21000071, 'xDataType': 1}, {'sID': 21000072, 'xDataType': 1}, {'sID': 21000073, 'xDataType': 1}, {'sID': 21000074, 'xDataType': 1}, {'sID': 21000075, 'xDataType': 1}, {'sID': 21000086, 'xDataType': 1}, {'sID': 21000087, 'xDataType': 1}, {'sID': 21000089, 'xDataType': 1}, {'sID': 21000090, 'xDataType': 1}, {'sID': 21000092, 'xDataType': 1}, {'sID': 21000094, 'xDataType': 1}, {'sID': 21000097, 'xDataType': 1}, {'sID': 21000098, 'xDataType': 1}, {'sID': 21000099, 'xDataType': 1}, {'sID': 21000100, 'xDataType': 1}, {'sID': 21000101, 'xDataType': 1}, {'sID': 21000102, 'xDataType': 1}, {'sID': 21000103, 'xDataType': 1}, {'sID': 21000104, 'xDataType': 1}, {'sID': 21000105, 'xDataType': 1}, {'sID': 21000106, 'xDataType': 1}, {'sID': 21000109, 'xDataType': 1}, {'sID': 21000110, 'xDataType': 1}, {'sID': 21000111, 'xDataType': 1}], 'msg': 'OK', 'qty': 64}, 'id': '8af174110a444909b1f449b9f10eaff7', 'message': '', 'receiver': 'checktoolclient_JTLD', 'sender': 'checktoolserver', 'timestamp': 1749043804465, 'topic': 'checktoolclient_JTLD'}
    # print("10101")
    # print(msg)
    ## 判断msg中是否有data这个属性,有的话, 取出来
    if "data" in msg :
        data = msg["data"]
        ## 判断data中是否有checks这个属性,有的话, 取出来.要留意data是否为空
        if data is not None and "checks" in data :
            checks = data["checks"]
            if checks is not None and len(checks) > 0 :
                checktool.docheck(checks)



@mqttbusiness(10208)
def handle_10208(msg : dict) :
    """
    10208 版本文件清单, 检查是否需要更新, 需要能够热更新 
    msg的格式是:
    {
        "business": 10208,
        "custcode": "JTLD",
        "data": {
            "clientversion": {
                "id" : 1234,
                "timestamp" : "2025-11-19 23:59:59",
                "name" : "2025年第一次版本更新",
                "baseurl" : "http://10.144.0.2:9000",
                "accesskey" : "wardyan",
                "secretkey" : "wardyan020598",
                "files" : [
                    {
                        "id" : 2234,
                        "objectkey": "origin_202206271928004549.jpg",
                        "path" : "/",
                        "name":"origin_202206271928004549.jpg",
                        "md5":"0000000000000",
                        "size":1234
                    },
                    {
                        "id" : 2234,
                        "objectkey": "origin_202206271928004549.jpg",
                        "path" : "/",
                        "name":"origin_202206271928004549.jpg",
                        "md5":"0000000000000",
                        "size":1234
                    }
                ]
            }
            ,
            "msg": "OK",
            "qty": 64
        },
        "id": "8af174110a444909b1f449b9f10eaff7",
        "message": "",
        "receiver": "checktoolclient_JTLD",
        "sender": "checktoolserver",
        "timestamp": 1749043804465,
        "topic": "checktoolclient_JTLD"
    }
    
    需求: 收到mqtt发过来的这个业务消息后, 根据消息内容, 去对象存储里下载文件回来. 注意, 首先要在跟目录创建一个临时文件夹temp,先把下载好的文件放到temp目录下,全部下载完后检查md5,全部OK后再移动到对应位置
    """
    print("处理10208业务消息")
    
    # 检查消息格式
    if "data" not in msg or "clientversion" not in msg["data"]:
        print("错误: 消息格式不正确，缺少data或clientversion字段")
        return
    
    clientversion = msg["data"]["clientversion"]
    
    # 提取对象存储配置
    baseurl = clientversion.get("baseurl", "")
    accesskey = clientversion.get("accesskey", "")
    secretkey = clientversion.get("secretkey", "")
    files = clientversion.get("files", [])
    
    print(f"对象存储配置: baseurl={baseurl}, accesskey={accesskey}, secretkey={secretkey}")
    print(f"需要下载的文件数量: {len(files)}")
    
    # 创建临时目录
    temp_dir = "temp"
    if not os.path.exists(temp_dir):
        os.makedirs(temp_dir)
        print(f"创建临时目录: {temp_dir}")
    
    # 下载文件并验证MD5
    downloaded_files = []
    for file_info in files:
        # 初始化变量
        objectkey = ""
        name = ""
        md5 = ""
        path = ""
        
        try:
            objectkey = file_info.get("objectkey", "")
            name = file_info.get("name", "")
            md5 = file_info.get("md5", "")
            path = file_info.get("path", "")
            
            print(f"开始下载文件: {name} (objectkey: {objectkey})")
            
            # 构造下载URL
            download_url = f"{baseurl}/{objectkey}"
            
            # 下载文件到临时目录
            temp_file_path = os.path.join(temp_dir, name)
            urllib.request.urlretrieve(download_url, temp_file_path)
            print(f"文件下载完成: {temp_file_path}")
            
            # 验证MD5
            if verify_md5(temp_file_path, md5):
                print(f"文件MD5验证通过: {name}")
                downloaded_files.append({
                    "temp_path": temp_file_path,
                    "final_path": name,  # 简化处理，实际应根据path确定最终位置
                    "info": file_info
                })
            else:
                print(f"文件MD5验证失败: {name}")
                # 删除验证失败的文件
                if os.path.exists(temp_file_path):
                    os.remove(temp_file_path)
        except Exception as e:
            print(f"下载或验证文件时出错: {name}, 错误: {e}")
    
    # 将验证通过的文件移动到最终位置
    for file_data in downloaded_files:
        try:
            temp_path = file_data["temp_path"]
            final_path = file_data["final_path"]
            
            # 确保目标目录存在
            final_dir = os.path.dirname(final_path)
            if final_dir and not os.path.exists(final_dir):
                os.makedirs(final_dir)
            
            # 移动文件
            shutil.move(temp_path, final_path)
            print(f"文件移动完成: {temp_path} -> {final_path}")
        except Exception as e:
            print(f"移动文件时出错: {file_data['temp_path']} -> {file_data['final_path']}, 错误: {e}")
    
    print("10208业务处理完成")


def verify_md5(file_path, expected_md5):
    """
    验证文件的MD5值
    """
    if not expected_md5 or expected_md5 == "0000000000000":
        # 如果没有提供MD5或使用默认值，则认为验证通过
        return True
    
    try:
        hash_md5 = hashlib.md5()
        with open(file_path, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
        calculated_md5 = hash_md5.hexdigest()
        return calculated_md5.lower() == expected_md5.lower()
    except Exception as e:
        print(f"计算文件MD5时出错: {e}")
        return False




























BUSINESSES = {
    10101 : handle_10101,
    10106 : handle_10106,
    10208 : handle_10208
}